from simulators.BaseSimulator import BaseSimulator
from simulators.Buckets import FlowRecord
import 文件工具
from 标准库 import 哈希, 累积序列


class HashPipe(BaseSimulator):
    def __init__(self, 内存=1.0, 导出周期=-1, 协议="ipv4"):
        # 内存方案也可以是一个整数或者浮数, 表示内存的大小, 单位是MB
        super(HashPipe, self).__init__()
        self.协议 = 协议
        self.内存 = 内存
        self.导出周期 = 导出周期
        self.哈希表 = []
        self.计算表长()
        for i in range(self.表长1 + self.表长2 + self.表长3 + self.表长4):
            self.哈希表.append(FlowRecord())

    def 计算表长(self):
        if self.周期模式 == self.模式 and "ipv4" == self.协议:
            tmp = int(self.内存*1024.0*1024.0/68.0)
        elif self.周期模式 == self.模式 and "ipv6" == self.协议:
            tmp = int(self.内存*1024.0*1024.0/164.0)
        elif self.超时模式 == self.模式:
            tmp = int(self.内存*1024.0*1024.0/72.0)
        else:
            raise SystemExit("计算表长失败!")
        self.表长1 = tmp
        self.表长2 = tmp
        self.表长3 = tmp
        self.表长4 = tmp

    def 计算导出周期(self, 带宽消耗):
        # 此处带宽消耗的单位是bps
        if "ipv4" == self.协议:
            流记录宽度 = 17.0*8  # 这里默认的流标识符是IPv4的五元组, 长度单位为"位"
        elif "ipv6" == self.协议:
            流记录宽度 = 41.0*8
        self.导出周期 = (流记录宽度*(self.表长1+self.表长2+self.表长3+self.表长4))/带宽消耗

    def 周期式数据包处理(self, 标识符, 时间戳):
        偏移量 = 累积序列([0, self.表长1, self.表长2, self.表长3])
        表长 = [self.表长1, self.表长2, self.表长3, self.表长4]
        计数值 = 1
        for i in range(4):
            索引 = 哈希(i, 标识符) % 表长[i] + 偏移量[i]
            if(self.哈希表[索引].empty()):
                self.哈希表[索引].init(标识符, 计数值)
                return
            elif (标识符 == self.哈希表[索引].flowID):
                self.哈希表[索引].increCount(计数值)
                return
            elif 0 == i or 计数值 > self.哈希表[索引].getCount():
                临时变量 = self.哈希表[索引].getRecord()
                self.哈希表[索引].init(标识符, 计数值)
                标识符, 计数值 = 临时变量

    def 超时式数据包处理(self, 标识符, 时间戳):
        偏移量 = 累积序列([0, self.表长1, self.表长2, self.表长3])
        表长 = [self.表长1, self.表长2, self.表长3, self.表长4]
        计数值 = 1
        for i in range(4):
            索引 = 哈希(i, 标识符) % 表长[i] + 偏移量[i]
            if(self.哈希表[索引].empty()):
                self.哈希表[索引].init(标识符, 计数值, 时间戳)
                return
            elif (标识符 == self.哈希表[索引].flowID):
                self.哈希表[索引].increCount(计数值, 时间戳)
                return
            else:
                if self.超时时间 > 0 and self.哈希表[索引].getTs() + self.超时时间 < 时间戳:
                    self.导出流记录(self.哈希表[索引].getRecord())
                    self.哈希表[索引].init(标识符, 1, 时间戳)
                    return
                if 0 == i or 计数值 > self.哈希表[索引].getCount():
                    临时变量 = self.哈希表[索引].getContent()
                    self.哈希表[索引].init(标识符, 计数值, 时间戳)
                    标识符, 计数值, 时间戳 = 临时变量

    def 计算索引(self, 标识符):
        偏移量 = 累积序列([0, self.表长1, self.表长2, self.表长3])
        表长 = [self.表长1, self.表长2, self.表长3, self.表长4]
        索引 = []
        for i in range(4):
            索引.append(哈希(i, 标识符) % 表长[i] + 偏移量[i])
        return 索引

    def 在线流长查询(self, 标识符):
        索引序列 = self.计算索引(标识符)
        流长 = 0
        for 索引 in 索引序列:
            if 标识符 == self.哈希表[索引].flowID:
                流长 += self.哈希表[索引].count
        return 流长

    def 下载数据平面(self):
        流集 = dict()
        for item in self.哈希表:
            if not item.empty():
                标识符, 计数值 = item.getRecord()
                if 标识符 not in 流集:
                    流集[标识符] = 0
                流集[标识符] += 计数值
        return 流集

    def 重置数据平面(self):
        for 索引 in range(self.表长1 + self.表长2 + self.表长3 + self.表长4):
            self.哈希表[索引].reset()

    def 备份(self, 文件名):
        备份 = dict()
        备份["流记录集"] = self.获取流记录集()
        备份["导出次数"] = self.导出次数
        文件工具.备份数据(备份, 文件名)
